import { cloneDeep } from 'lodash'
import _ from '@/assets/utils'

/* createStore：创建store容器的 */
export const createStore = function createStore(reducer) {
    if (typeof reducer !== 'function') throw new TypeError(`reducer必须是一个函数`)
    // 公共状态
    let state
    // 事件池
    let listeners = []

    // 获取公共状态
    const getState = function getState() {
        // redux源码本身存在一个BUG：基于getState获取的公共状态信息，和容器中的state是相同的堆内存，这样在组件中，当我们获取公共状态后，可以绕过dispatch派发，直接通过state.xxx=xxx修改公共状态信息！
        // return state
        // 所以我们把返回的状态信息，最好进行深拷贝「弊端：浪费性能」
        return cloneDeep(state)
    }

    // 向事件池中加入方法
    const subscribe = function subscribe(callback) {
        if (typeof callback !== 'function') throw new TypeError('callback必须是函数')
        if (!listeners.includes(callback)) {
            listeners.push(callback)
        }
        // 返回从事件池中移除函数的方法
        return function unsubscribe() {
            let index = listeners.indexOf(callback)
            if (index >= 0) {
                listeners.splice(index, 1)
            }
        }
    }

    // 任务派发，通知reducer执行
    const dispatch = function dispatch(action) {
        if (!_.isPlainObject(action)) throw new TypeError('action必须是一个标准对象')
        if (!('type' in action)) throw new TypeError('action对象必须具备type属性')
        // 通知reducer执行：修改公共状态
        state = reducer(state, action)
        // 公共状态更改，我们需要通知事件池中的方法执行
        let arr = listeners.slice()
        arr.forEach(listener => {
            if (typeof listener === 'function') listener()
        })
    }

    // 最开始默认：我们需要派发第一次，其目的是设置初始状态信息
    dispatch({
        type: Symbol('INIT-STATE')
    })

    // 返回store对象
    return {
        getState,
        subscribe,
        dispatch
    }
}